﻿using System;
using System.Linq;
using System.Reflection;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Localization;
using Volo.Abp.Reflection;

namespace Xms.Abp.Authorization;

public class XmsPermissionDefinitionProvider : PermissionDefinitionProvider
{
    private readonly ITypeFinder _typeFinder;

    public XmsPermissionDefinitionProvider(ITypeFinder typeFinder)
    {
        _typeFinder = typeFinder;
    }

    public override void Define(IPermissionDefinitionContext context)
    {
        var permissionTypes = _typeFinder.Types.Where(type =>
            type.IsPublic &&
            !type.IsAbstract &&
            type.IsClass &&
            typeof(IPermissionsConst).IsAssignableFrom(type) &&
            type.GetCustomAttribute<PermissionDefinitionAttribute>() != null
        );
        foreach (var permissionType in permissionTypes)
        {
            var attr = permissionType.GetCustomAttribute<PermissionDefinitionAttribute>();
            var define = context.AddGroup(PermissionsHelper.GetTypeName(permissionType), DisplayName(attr.LocalizationResource, permissionType), attr.MultiTenancySides);
            RecursivelyDefinition(define, attr.LocalizationResource, permissionType.GetNestedTypes(), PermissionsHelper.GetTypeName(permissionType));
        }
    }

    public void RecursivelyDefinition(PermissionGroupDefinition group, Type resourceType, Type[] types, string nameSpace)
    {
        foreach (var type in types)
        {
            var attr = type.GetCustomAttribute<PermissionItemAttribute>();
            var define = group.AddPermission(
                PermissionsHelper.GetSubPermissionName(type, nameSpace),
                displayName: DisplayName(resourceType, type, nameSpace),
                multiTenancySide: attr.MultiTenancySides,
                isEnabled: attr.IsEnabled);
            RecursivelyDefinition(define, resourceType, type.GetNestedTypes(), nameSpace + "." + PermissionsHelper.GetTypeName(type));
            SetConstsDefinition(define, resourceType, type, nameSpace);
        }
    }

    public void RecursivelyDefinition(PermissionDefinition define, Type resourceType, Type[] types, string nameSpace)
    {
        foreach (var type in types)
        {
            var attr = type.GetCustomAttribute<PermissionItemAttribute>();
            var def = define.AddChild(
                PermissionsHelper.GetSubPermissionName(type, nameSpace),
                displayName: DisplayName(resourceType, type, nameSpace),
                multiTenancySide: attr.MultiTenancySides,
                isEnabled: attr.IsEnabled);
            RecursivelyDefinition(def, resourceType, type.GetNestedTypes(), nameSpace + "." + PermissionsHelper.GetTypeName(type));
            SetConstsDefinition(define, resourceType, type, nameSpace);
        }
    }

    private void SetConstsDefinition(PermissionDefinition define, Type resourceType, Type type, string nameSpace)
    {
        var properties = type.GetFields().Where(r=>r.IsLiteral && !r.IsInitOnly);
        foreach (var property in properties)
        {
            var propertyName = property.Name;
            var attr = property.GetCustomAttribute<PermissionItemAttribute>();
            define.AddChild(
                PermissionsHelper.GetSubPermissionName(type, nameSpace, propertyName),
                displayName: DisplayName(resourceType, type, nameSpace, propertyName),
                multiTenancySide: attr.MultiTenancySides,
                isEnabled: attr.IsEnabled
                );
        }
    }

    private static LocalizableString L(Type resourceType, string key)
    {
        return new LocalizableString(resourceType, key);
    }

    private static LocalizableString DisplayName(Type resourceType, Type type, string nameSpace = null, string propertyName = null)
    {
        return L(resourceType, PermissionsHelper.GetDisplayNameKey(type, nameSpace, propertyName));
    }
}
